package licode.unisop.auth.infrastructure.auth;

import licode.unisop.auth.application.gateway.UserAuthGateway;
import licode.unisop.auth.client.info.Credential;
import licode.unisop.auth.exception.AuthErrorCode;
import licode.unisop.client.info.SopTokenInfo;
import licode.unisop.client.info.SopUserIdIn;
import licode.unisop.client.vo.SopUserAssets;
import licode.unisop.oidc.auth.conf.AuthorDef;
import licode.unisop.oidc.auth.conf.GrantType;
import licode.unisop.oidc.auth.modal.AuthorRedirectUrl;
import licode.unisop.oidc.auth.stu.CheckUtil;
import licode.unisop.oidc.auth.utils.StringTool;
import licode.unisop.oidc.auth.utils.UrlTool;
import licode.unisop.provider.api.SiOauthProvider;
import licode.unisop.provider.info.*;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Map;

/**
 * 实现用户认证的接口
 *
 * @author WR
 */
@Primary
@Service
public class UserAuthGatewayDefault implements UserAuthGateway {
    @Resource
    private SiOauthProvider siOauthProvider;

    private SiOauthProvider getOauthPlugin() {
        return siOauthProvider;
    }

    @Override
    public boolean renewAuthUser(String accessToken) {
        return false;
    }

    @Override
    public Credential buildAuthInfo(SiAuthCode codeInfo) {
        Map<String, Object> data;
        data = getOauthPlugin().buildCredential(codeInfo);
        if (null == data) {
            throw AuthErrorCode.AUTH_CODE_HAS_EXPIRED.buildThrow();
        }
        return fromOidcToken(data);
    }

    @Override
    public SiCodeInfo authorize(SiAuthContextIn authorReq) {
        SiCodeInfo code = null;
        String redirectUri = null;

        if (GrantType.AUTHORIZATION_CODE.equals(authorReq.getGrantType())) {
            code = getOauthPlugin().buildCode(authorReq);

            /*
             * 构建重定向地址
             */
            redirectUri = calcRedirectUri(code, authorReq.getTicketUri(), authorReq.getState());
        }

        if (null != code) {
            code.setRedirectUri(redirectUri);
        }

        return code;
    }

    @Override
    public SiCodeInfo autoSso(SiAuthContextIn auth) {
        // 根据主会话找到已经登录的信息
        SiCodeInfo code;
        code = getOauthPlugin().autoSso(auth);

        if (null != code) {
            code.setRedirectUri(calcRedirectUri(code, auth.getTicketUri(), auth.getState()));
        }

        return code;
    }

    private String calcRedirectUri(SiCodeInfo code, String ticketUri, String state) {
        /*
         * 检查重定向地址的合法性
         */
        CheckUtil.getAuthChecker().checkRedirectUrl(new AuthorRedirectUrl(ticketUri));

        /*
         * 构建重定向地址
         */
        return buildRedirectUri(ticketUri, code.getCode(), state);
    }

    /**
     * 注意，调用这个方法时，用户和client都是认证过的，
     * 我们只需要生成认证信息即可
     *
     * @param contextIn 认证信息
     */
    @Override
    public Credential buildAuthInfo(SiAuthContextIn contextIn) {
        Credential credential = null;
        if (GrantType.AUTHORIZATION_CODE.equals(contextIn.getAuthMode())) {
        } else {
            credential = fromOidcToken(getOauthPlugin().buildCredential(contextIn));
        }
        return credential;
    }

    @Override
    public Credential refreshAuthInfo(SopTokenInfo sopTokenInfo) {
        return fromOidcToken(getOauthPlugin().refresh(TokenIdInfo
                .builder()
                .token(sopTokenInfo.getToken())
                .realmId(sopTokenInfo.getRealmId())
                .platformId(sopTokenInfo.getPlatformId())
                .build(), true));
    }

    @Override
    public void logout(SopTokenInfo sopTokenInfo) {
        getOauthPlugin().logoutByAccessToken(TokenIdInfo
                .builder()
                .token(sopTokenInfo.getToken())
                .realmId(sopTokenInfo.getRealmId())
                .platformId(sopTokenInfo.getPlatformId())
                .build());
    }

    @Override
    public void logoutSso(SopTokenInfo sopTokenInfo) {
        getOauthPlugin().logoutBySso(TokenIdInfo
                .builder()
                .token(sopTokenInfo.getToken())
                .realmId(sopTokenInfo.getRealmId())
                .platformId(sopTokenInfo.getPlatformId())
                .build());
    }

    @Override
    public void logoutUser(SopUserIdIn req) {
        getOauthPlugin().logoutByUserId(UserIdInfo
                .builder()
                .userId(req.getUserId())
                .realmId(req.getRealmId())
                .platformId(req.getPlatformId())
                .build());
    }

    @Override
    public void logoutSession(SopTokenInfo sopTokenInfo) {
        getOauthPlugin().logoutBySession(TokenIdInfo
                .builder()
                .token(sopTokenInfo.getToken())
                .realmId(sopTokenInfo.getRealmId())
                .platformId(sopTokenInfo.getPlatformId())
                .build());
    }

    @Override
    public void logoutByUserId(String userId) {
        getOauthPlugin().logoutByUserId(UserIdInfo
                .builder()
                .userId(userId)
                .realmId("default")
                .platformId("default")
                .build());
    }

    @Override
    public boolean userIsLogin(String accessToken) {
        return getOauthPlugin().isLogin(TokenIdInfo
                .builder()
                .token(accessToken)
                .build());
    }

    @Override
    public SopUserAssets getUserAssets(SopUserIdIn idInfo) {
        return null;
    }

    public String buildRedirectUri(String redirectUri, String code, String state) {
        String url = UrlTool.joinParam(redirectUri, AuthorDef.CODE, code);
        if (!StringTool.isEmpty(state)) {
            url = UrlTool.joinParam(url, AuthorDef.STATE, state);
        }
        return url;
    }

    private Credential fromOidcToken(Map<String, Object> oidcToken) {
        Credential credential = null;
        if (null != oidcToken) {
            credential = new Credential();
            BeanMap beanMap = BeanMap.create(credential);
            beanMap.putAll(oidcToken);
        }
        return credential;
    }
}
